use crate::{
    action::record::{
        count_all_records, count_records, list_all_records, list_records, ListRecordError,
        RecordTypeExt,
    },
    data::message::Message,
    fairing::record::RecordDb,
    model::user::UserData,
};
use network_scanner_server_model::{Record, RecordTypeId};
use rocket::http::Status;
use rocket::request::Request;
use rocket::response::{self, Responder, Response};
use rocket::route::Route;
use rocket::serde::{json::Json, Serialize};
use rocket_db_pools::{
    sqlx::{self, Acquire},
    Connection,
};

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct ListRecordResult {
    list: Vec<Record>,
    full_length: u32,
}

enum ListRecordStatus {
    Ok(ListRecordResult),
    Failed(sqlx::Error),
    TypeNotFound,
}

impl From<sqlx::Error> for ListRecordStatus {
    fn from(value: sqlx::Error) -> Self {
        ListRecordStatus::Failed(value)
    }
}

impl From<ListRecordError> for ListRecordStatus {
    fn from(value: ListRecordError) -> Self {
        match value {
            ListRecordError::TypeNotFound(_) => ListRecordStatus::TypeNotFound,
            ListRecordError::Database(err) => ListRecordStatus::Failed(err),
        }
    }
}

impl<'r> Responder<'r, 'static> for ListRecordStatus {
    fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
        match self {
            ListRecordStatus::Ok(users) => Response::build_from(Json(users).respond_to(req)?)
                .status(Status::Ok)
                .ok(),
            ListRecordStatus::Failed(err) => Response::build_from(
                Json(Message {
                    message: err.to_string(),
                })
                .respond_to(req)?,
            )
            .status(Status::InternalServerError)
            .ok(),
            ListRecordStatus::TypeNotFound => Response::build().status(Status::NotFound).ok(),
        }
    }
}

#[get("/?<start>&<len>&<type>", rank = 1)]
async fn list(
    mut db: Connection<RecordDb>,
    _user: UserData,
    start: u32,
    len: u32,
    r#type: String,
) -> ListRecordStatus {
    let result = async {
        let mut tx = db.begin().await?;

        let type_id = RecordTypeId::get_by_name(r#type.as_str()).await;
        let type_id = match type_id {
            Some(type_id) => type_id,
            None => return Err(ListRecordStatus::TypeNotFound),
        };

        let full_length = count_records(&mut tx, type_id).await?.unwrap();
        let list = list_records(&mut tx, type_id, start, len).await?;

        tx.commit().await?;
        Ok(ListRecordStatus::Ok(ListRecordResult { list, full_length }))
    }
    .await;
    match result {
        Ok(status) => status,
        Err(err) => err,
    }
}

#[get("/?<start>&<len>", rank = 0)]
async fn list_all(
    mut db: Connection<RecordDb>,
    _user: UserData,
    start: u32,
    len: u32,
) -> ListRecordStatus {
    let result = async {
        let mut tx = db.begin().await?;

        let full_length = count_all_records(&mut tx).await?;
        let list = list_all_records(&mut tx, start, len).await?;

        tx.commit().await?;
        Ok(ListRecordStatus::Ok(ListRecordResult { list, full_length }))
    }
    .await;
    match result {
        Ok(status) => status,
        Err(err) => err,
    }
}

pub fn route() -> Vec<Route> {
    routes![list, list_all]
}
